﻿using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Xml.Linq;

using ProjectBase.Core;
using ProjectBase.Tools.Publishing;
using $safeprojectname$.Infrastructure.Exceptions;
using $safeprojectname$.Infrastructure.Factories;

using log4net.Core;

using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace $safeprojectname$.Publishing.Handlers
{
    [TestClass]
    public class Log4NetHandlerTest : TestBase
    {
        [TestMethod]
        public void StringMessage_Passed()
        {
            var provider = ProviderFactory.ProviderLog4NetOnly() as DefaultPublishingProvider;
            var handler = provider.Handler;

            provider.Publish(this, Severity.Error, "MyMessage: '{0}'", "1) provider calls handler");

            var appender = LoggingTargets.GetCurrentMemoryAppender();
            var result = appender.GetEvents().First();

            Assert.IsTrue(result.RenderedMessage == "MyMessage: '1) provider calls handler'", result.RenderedMessage);
            Assert.IsTrue(result.Level == Level.Error);

            appender.Clear();
            handler.Handle(null, Severity.Debug, "MyMessage: '{0}'", "2) handler directly");
            var result2 = appender.GetEvents().First();
            Assert.IsTrue(result2.RenderedMessage == "MyMessage: '2) handler directly'", result.RenderedMessage);
            Assert.IsTrue(result2.Level == Level.Debug);
        }

        [TestMethod]
        public void StringDelegate_Passed()
        {
            var handler = new Log4NetPublishingHandler();
            handler.Init(Log4NetXElement());

            handler.Handle(null, () => "MyMessage {0}".FormatWith("Handler"), Severity.Info);

            var appender = LoggingTargets.GetCurrentMemoryAppender();
            var result = appender.GetEvents().First();
            Assert.IsTrue(result.RenderedMessage == "MyMessage Handler", result.RenderedMessage);
            Assert.IsTrue(result.Level == Level.Info);

            handler.Exit();
        }

        [TestMethod]
        public void Exception_Passed()
        {
            var handler = new Log4NetPublishingHandler();
            handler.Init(Log4NetXElement());

            handler.Handle(null, null, new ChildException("ChildException"));


            var appender = LoggingTargets.GetCurrentMemoryAppender();
            var result = appender.GetEvents().First();

            Assert.IsTrue(result.RenderedMessage == "ChildException", result.RenderedMessage);
            Assert.IsTrue(result.Level == Level.Fatal);

            handler.Exit();
        }

        [TestMethod]
        public void Null_Passed()
        {
            var handler = new Log4NetPublishingHandler();
            handler.Init(Log4NetXElement());

            ChildException exception = null;
            Func<string> nullDelegate = null;

            handler.Handle(this, "ExceptionMessage", exception);
            handler.Handle(this, Severity.Error, null, null);
            handler.Handle(this, nullDelegate, Severity.Info);

            var appender = LoggingTargets.GetCurrentMemoryAppender();
            var result = appender.GetEvents().FirstOrDefault();
            Assert.IsTrue(result.IsNull());

            handler.Exit();
        }

        [TestMethod]
        public void BrokenDelegate_Failed()
        {
            var handler = new Log4NetPublishingHandler();
            handler.Init(Log4NetXElement());

            Func<string> nullDelegate = () => "Format me {0} {1}".FormatWith("only");

            handler.Handle(this, nullDelegate, Severity.Info);

            var appender = LoggingTargets.GetCurrentMemoryAppender();
            var result = appender.GetEvents().FirstOrDefault();

            Assert.IsTrue(result.RenderedMessage.Contains("Log4Net PublishingHandler: Cannot Invoke() provided Func<string>"), result.RenderedMessage);

            handler.Exit();
        }

        #region protected
        protected virtual XElement Log4NetXElement()
        {
            var element = new XElement("settings");
            var log4NetConfig = new XElement(ProjectBase.Tools.Publishing.Handlers.Constants.ElementLog4NetConfig);
            var value = new XAttribute("value", "Config/Log4Net.config");
            log4NetConfig.Add(value);
            element.Add(log4NetConfig);
            return element;
        }
        #endregion protected

        #region Init

        // You can use the following additional attributes as you write your tests:

        // Use ClassInitialize to run code before running the first test in the class
        // [ClassInitialize()]
        // public static void MyClassInitialize(TestContext testContext) { }

        // Use ClassCleanup to run code after all tests in a class have run
        // [ClassCleanup()]
        // public static void MyClassCleanup() { }

        [TestInitialize]
        public override void MyTestInitialize()
        {
            base.MyTestInitialize(); // Use TestInitialize to run code before running each test
        }

        [TestCleanup]
        public override void MyTestCleanup()
        {
            base.MyTestCleanup(); // Use TestCleanup to run code after each test has run
        }
        #endregion Init
    }
}
